/**
 * Copyright (c) 2011-2019 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <cstdint>
#include <bitcoin/system/settings.hpp>
#include <bitcoin/system/chain/block.hpp>
#include <bitcoin/system/chain/chain_state.hpp>

namespace libbitcoin {
namespace system {

using namespace chain;

// Common default values (no settings context).
settings::settings()
  : timestamp_limit_seconds(2 * 60 * 60),
    first_version(1),
    bip34_version(2),
    bip66_version(3),
    bip65_version(4),
    bip9_version_bit0(1u << 0),
    bip9_version_bit1(1u << 1),
    bip9_version_base(0x20000000),
    retargeting_factor_(4),
    block_spacing_seconds_(10 * 60),
    retargeting_interval_seconds_(2 * 7 * 24 * 60 * 60),
    minimum_timespan_(chain_state::minimum_timespan(
        retargeting_interval_seconds_, retargeting_factor_)),
    maximum_timespan_(chain_state::maximum_timespan(
        retargeting_interval_seconds_, retargeting_factor_)),
    retargeting_interval_(chain_state::retargeting_interval(
        retargeting_interval_seconds_, block_spacing_seconds_)),
    initial_block_subsidy_bitcoin_(50),
    recursive_money_(9999999989u)
{
}

settings::settings(config::settings context)
  : settings()
{
    switch (context)
    {
        case config::settings::mainnet:
        {
            proof_of_work_limit = 0x1d00ffff;
            genesis_block = chain::block::factory({
                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd,
                0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e,
                0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3,
                0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa,
                0x4b, 0x1e, 0x5e, 0x4a, 0x29, 0xab, 0x5f, 0x49,
                0xff, 0xff, 0x00, 0x1d, 0x1d, 0xac, 0x2b, 0x7c,
                0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d,
                0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54,
                0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f,
                0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39,
                0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c,
                0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62,
                0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20,
                0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62,
                0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66,
                0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73,
                0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05,
                0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04,
                0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27,
                0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10,
                0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6,
                0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6,
                0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4,
                0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde,
                0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57,
                0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f,
                0xac, 0x00, 0x00, 0x00, 0x00});
            activation_threshold = 750;
            enforcement_threshold = 950;
            activation_sample = 1000;
            bip65_freeze = 388381;
            bip66_freeze = 363725;
            bip34_freeze = 227931;
            bip16_activation_time = 0x4f779a80;
            bip34_active_checkpoint = config::checkpoint(
                "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8",
                bip34_freeze);
            bip9_bit0_active_checkpoint = config::checkpoint(
                "000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5",
                419328);
            bip9_bit1_active_checkpoint = config::checkpoint(
                "0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893",
                481824);

            subsidy_interval_ = 210000;
            break;
        }

        case config::settings::testnet:
        {
            proof_of_work_limit = 0x1d00ffff;
            genesis_block = chain::block::factory({
                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd,
                0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e,
                0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3,
                0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa,
                0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d,
                0xff, 0xff, 0x00, 0x1d, 0x1a, 0xa4, 0xae, 0x18,
                0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d,
                0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54,
                0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f,
                0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39,
                0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c,
                0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62,
                0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20,
                0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62,
                0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66,
                0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73,
                0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05,
                0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04,
                0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27,
                0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10,
                0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6,
                0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6,
                0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4,
                0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde,
                0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57,
                0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f,
                0xac, 0x00, 0x00, 0x00, 0x00});
            activation_threshold = 51;
            enforcement_threshold = 75;
            activation_sample = 100;
            bip65_freeze = 581885;
            bip66_freeze = 330776;
            bip34_freeze = 21111;
            bip16_activation_time = 0x4f3af580;
            bip34_active_checkpoint = config::checkpoint(
                "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8",
                bip34_freeze);
            bip9_bit0_active_checkpoint = config::checkpoint(
                "00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb",
                770112);
            bip9_bit1_active_checkpoint = config::checkpoint(
                "00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca",
                834624);

            subsidy_interval_ = 210000;
            break;
        }

        case config::settings::regtest:
        {
            proof_of_work_limit = 0x207fffff;
            genesis_block = chain::block::factory({
                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd,
                0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e,
                0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3,
                0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa,
                0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d,
                0xff, 0xff, 0x7f, 0x20, 0x02, 0x00, 0x00, 0x00,
                0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d,
                0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54,
                0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f,
                0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39,
                0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c,
                0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62,
                0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20,
                0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62,
                0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66,
                0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73,
                0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05,
                0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04,
                0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27,
                0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10,
                0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6,
                0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6,
                0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4,
                0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde,
                0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57,
                0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f,
                0xac, 0x00, 0x00, 0x00, 0x00});
            bip65_freeze = 1351;
            bip66_freeze = 1251;
            bip34_freeze = 0;
            bip16_activation_time = 0x4f3af580;
            const config::checkpoint genesis_checkpoint(
                static_cast<chain::block>(genesis_block).hash(), 0);

            // bip90 assumes a historical bip34 activation block, use genesis.
            bip34_active_checkpoint = genesis_checkpoint;

            // This is fixed and closed, so assume genesis activation.
            bip9_bit0_active_checkpoint = genesis_checkpoint;
            bip9_bit1_active_checkpoint = genesis_checkpoint;

            subsidy_interval_ = 150;
            break;
        }

        default:
        case config::settings::none:
        {
        }
    }
    max_money_ = recursive_money_ * subsidy_interval_;
}

void settings::retargeting_factor(uint32_t value)
{
    retargeting_factor_ = value;
    minimum_timespan_ = chain_state::minimum_timespan(
        retargeting_interval_seconds_, retargeting_factor_);
    maximum_timespan_ = chain_state::maximum_timespan(
        retargeting_interval_seconds_, retargeting_factor_);
}

uint32_t settings::retargeting_interval_seconds() const
{
    return retargeting_interval_seconds_;
}

void settings::retargeting_interval_seconds(uint32_t value)
{
    retargeting_interval_seconds_ = value;
    minimum_timespan_ = chain_state::minimum_timespan(
        retargeting_interval_seconds_, retargeting_factor_);
    maximum_timespan_ = chain_state::maximum_timespan(
        retargeting_interval_seconds_, retargeting_factor_);
    retargeting_interval_ = chain_state::retargeting_interval(
        retargeting_interval_seconds_, block_spacing_seconds_);
}

uint32_t settings::block_spacing_seconds() const
{
    return block_spacing_seconds_;
}

void settings::block_spacing_seconds(uint32_t value)
{
    block_spacing_seconds_ = value;
    retargeting_interval_ = chain_state::retargeting_interval(
        retargeting_interval_seconds_, block_spacing_seconds_);
}

uint32_t settings::minimum_timespan() const
{
    return minimum_timespan_;
}

uint32_t settings::maximum_timespan() const
{
    return maximum_timespan_;
}

size_t settings::retargeting_interval() const
{
    return retargeting_interval_;
}

uint64_t settings::bitcoin_to_satoshi(uint64_t value) const
{
    static const uint64_t satoshi_per_bitcoin = 100000000;
    return value * satoshi_per_bitcoin;
}

void settings::initial_block_subsidy_bitcoin(uint64_t value)
{
    initial_block_subsidy_bitcoin_ = value;
    const std::function<uint64_t(uint64_t)> recursive_money =
        [&recursive_money](uint64_t money)
        {
            return money > 0 ? money + recursive_money(money >> 1u) : 0;
        };
    recursive_money_ = recursive_money(bitcoin_to_satoshi(value));

    //**************************************************************************
    // CONSENSUS: This is the true maximum amount of money that can be created.
    // The satoshi client uses a "sanity check" value that is effectively based
    // on a round but incorrect value of recursive_money, which is higher than
    // this true value. Despite comments to the contrary in the satoshi code, no
    // value could be consensus critical unless it was *less* than the true
    // value.
    //**************************************************************************
    max_money_ = recursive_money_ * subsidy_interval_;
}

uint64_t settings::initial_block_subsidy_bitcoin() const
{
    return initial_block_subsidy_bitcoin_;
}

void settings::subsidy_interval(uint64_t value)
{
    subsidy_interval_ = value;
    max_money_ = recursive_money_ * subsidy_interval_;
}

uint64_t settings::subsidy_interval() const
{
    return subsidy_interval_;
}

uint64_t settings::max_money() const
{
    return max_money_;
}

} // namespace system
} // namespace libbitcoin
